package com.permissions.dispatcher.processor.util;


import com.permissions.dispatcher.annotation.NeedsPermission;
import com.permissions.dispatcher.processor.PermissionsProcessor;
import com.permissions.dispatcher.processor.RuntimePermissionsElement;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.TypeName;

import java.lang.annotation.Annotation;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;

import static com.permissions.dispatcher.processor.util.Constants.*;


/**
 * 帮助类
 */
public class Helpers {
    /**
     * 获取请求码文件名
     * @param executableElement executableElement
     * @return String
     */
    public static String requestCodeFieldName(ExecutableElement executableElement) {
        return GEN_REQUEST_CODE_PREFIX + Extensions.trimDollarIfNeeded(
                Extensions.simpleString(executableElement)).toUpperCase(Locale.ROOT);
    }

    /**
     * 权限名
     * @param executableElement executableElement
     * @return String
     */
    public static String permissionFieldName(ExecutableElement executableElement) {
        return GEN_PERMISSION_PREFIX + Extensions.trimDollarIfNeeded(
                Extensions.simpleString(executableElement)).toUpperCase(Locale.ROOT);
    }
    /**
     * 待处理请求名称
     * @param executableElement executableElement
     * @return String
     */
    public static String pendingRequestFieldName(ExecutableElement executableElement) {
        return GEN_PENDING_PREFIX + Extensions.trimDollarIfNeeded(
                Extensions.simpleString(executableElement)).toUpperCase(Locale.ROOT);
    }
    /**
     * 全程检查名
     * @param executableElement executableElement
     * @return String
     */
    public static String withPermissionCheckMethodName(ExecutableElement executableElement) {
        return Extensions.trimDollarIfNeeded(
                Extensions.simpleString(executableElement)) + GEN_WITH_PERMISSION_CHECK_SUFFIX;
    }

    /**
     * 参数域名
     * @param element element
     * @param arg arg
     * @return String
     */
    public static String argumentFieldName(Element element, Element arg) {
        return Extensions.simpleString(element) + Extensions.simpleString(arg);
    }

    /**
     * 处理OnShowRationaleMethod
     * @param element element
     * @return String
     */
    public static String proceedOnShowRationaleMethodName(Element element) {
        return "proceed" + Extensions.simpleString(element) + GEN_PERMISSION_REQUEST_SUFFIX;
    }

    /**
     * 取消OnShowRationaleMethod
     * @param element element
     * @return String
     */
    public static String cancelOnShowRationaleMethodName(Element element) {
        return "cancel" + Extensions.simpleString(element) + GEN_PERMISSION_REQUEST_SUFFIX;
    }

    /**
     * 权限请求类型名
     * @param runtimePermissionsElement runtimePermissionsElement
     * @param element element
     * @return String
     */
    public static String permissionRequestTypeName(
            RuntimePermissionsElement runtimePermissionsElement, Element element) {
        return runtimePermissionsElement.getInputClassName()
                + Extensions.simpleString(element)
                + GEN_PERMISSION_REQUEST_SUFFIX;
    }

    /**
     * 执行方法 ExecutableElement
     * @param needsElement needsElement
     * @param onRationaleElements onRationaleElements
     * @param annotationType annotationType
     * @param <A> <A>
     * @return ExecutableElement
     */
    public static <A extends Annotation> ExecutableElement findMatchingMethodForNeeds(
            ExecutableElement needsElement, List<ExecutableElement> onRationaleElements, Class<A> annotationType) {
        if (needsElement == null) {
            return null;
        }
        List<String> value = Extensions.permissionValue(needsElement.getAnnotation(NeedsPermission.class));
        if (onRationaleElements != null && onRationaleElements.size() > 0) {
            for (ExecutableElement element : onRationaleElements) {
                List<String> list = Extensions.permissionValue(element.getAnnotation(annotationType));
                // 比较两个list里面的值是否相等
                if (checkDiff(value, list)) {
                    return element;
                }
            }
        }

        return null;
    }

    private static boolean checkDiff(List<String> list, List<String> list1) {
        list.sort(Comparator.comparing(String::hashCode));
        list1.sort(Comparator.comparing(String::hashCode));
        return list.toString().equals(list1.toString());
    }

    /**
     * varargsParametersCodeBlock
     * @param executableElement e
     * @param withCache withCache
     * @return CodeBlock
     */
    public static CodeBlock varargsParametersCodeBlock(ExecutableElement executableElement, boolean withCache) {
        CodeBlock.Builder builder = CodeBlock.builder();
        for (int i = 0; i < executableElement.getParameters().size(); i++) {
            VariableElement it = executableElement.getParameters().get(i);
            String name;
            if (withCache) {
                name = argumentFieldName(executableElement, it);
            } else {
                name = Extensions.simpleString(it);
            }
            builder.add("$L", name);
            if (i < executableElement.getParameters().size() - 1) {
                builder.add(", ");
            }
        }

        return builder.build();
    }

    /**
     * varargsParametersCodeBlock
     * @param executableElement executableElement
     * @return CodeBlock
     */
    public static CodeBlock varargsParametersCodeBlock(ExecutableElement executableElement) {
        return varargsParametersCodeBlock(executableElement, false);
    }

    /**
     * 类型名
     * @param element element
     * @return TypeName
     */
    public static TypeName typeNameOf(Element element) {
        return TypeName.get(element.asType());
    }

    /**
     * typeMirrorOf
     * @param className className
     * @return TypeMirror
     */
    public static TypeMirror typeMirrorOf(String className) {
        return PermissionsProcessor.ELEMENT_UTILS.getTypeElement(className).asType();
    }
}
